;; protocol/osc-encode.scm - (c) rohan drape, 2002-2005

;; Evaluates to the OSC <u8vector> representing the <string> `s'.
;; OSC strings are zero terminated, and end at a four byte boundary.

(define (osc-encode-string s)
  (u8vector-append (string->u8vector s)
		   (make-u8vector (- 4 (modulo (string-length s) 4))
				  0)))

;; Evaluates to the OSC <u8vector> representing the <u8vector>
;; `b'.  OSC <u8vector>s are length prefixed with a four byte signed
;; network order integer, and zero byte padded to a four byte
;; boundary.

(define (osc-encode-bytes b)
  (let ((n (u8vector-length b)))
    (u8vector-append (encode-int32 n)
		     b 
		     (make-u8vector (- 4 (modulo n 4))
				    0))))

;; Evaluates to the OSC <u8vector> representing the object `e'.
;; The value `e' must be of type <integer>, <real>, <string>, or
;; <u8vector>.  Note further that determining if a <real> should be
;; written as a float or a double is non-trivial and not undertaken
;; here, all <real>s are written as floats.

(define (osc-encode-value e)
  (cond ((exactI? e) (encode-int32 e))
	((real? e) (encode-float32 e))
	((string? e) (osc-encode-string e))
	((u8vector? e) (osc-encode-bytes e))
	(else (error "osc-encode-value: Illegal object type" e))))

;; Evaluates to the OSC <u8vector> indicating the types of the
;; elements of the list `l'.

(define (osc-encode-type-descriptor l)
  (osc-encode-string 
   (apply
    string 
    #\, 
    (map (lambda (e) 
	   (cond ((exactI? e) #\i)
		 ((real? e) #\f)
		 ((string? e) #\s)
		 ((u8vector? e) #\b)
		 (else (error "osc-encode-type-descriptor: Illegal object" e))))
	 l))))

;; Evaluates to the OSC <u8vector> encoding the scheme OSC message
;; `message'.  A scheme OSC message is a <list>, the first element is
;; an address <string>, subsequent elements are <integer>, <real>,
;; <string> and <u8vector> arguments.

(define (osc-encode-message message)
  (u8vector-append
   (osc-encode-string (car message))
   (osc-encode-type-descriptor (cdr message))
   (u8vector-concatenate (map osc-encode-value (cdr message)))))

;; Evaluates to the OSC <u8vector> encoding the scheme OSC bundle
;; `bundle'.  A scheme OSC bundle is a <list>, the first element is a
;; <real> valued UTC 'time-tag', each subsequent element must be an
;; OSC 'message'.

(define (osc-encode-bundle bundle)
  (u8vector-append
   (osc-encode-string "#bundle")
   (encode-uint64 (utc->ntp (car bundle)))
   (u8vector-concatenate
    (map (lambda (e)
	   (if (osc-lisp-message? e)
	       (let ((message (osc-encode-message e)))
		 (u8vector-append (encode-int32 
				   (u8vector-length message))
				  message))
	       (error "osc-encode-bundle: illegal element" e)))
	 (cdr bundle)))))

;; Evaluates to the OSC <u8vector> encoding the scheme OSC packet
;; `packet'.  A scheme OSC packet is either a scheme OSC message or a
;; scheme OSC bundle.

(define (osc-encode-packet packet)
  (if (osc-lisp-bundle? packet)
      (osc-encode-bundle packet)
      (osc-encode-message packet)))

